home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / vim / src / termlib.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  16KB  |  648 lines

  1. /* vi:sw=4:ts=4:
  2.    The following software is (C) 1984 Peter da Silva,
  3.    the Mad Australian, in the public domain. It may
  4.    be re-distributed for any purpose with the inclusion
  5.    of this notice. */
  6.  
  7. /* modified by Bram Moolenaar for use with VIM - Vi Improved */
  8.  
  9. /* TERMLIB: Terminal independant database. */
  10.  
  11. #include "vim.h"
  12. #include "proto.h"
  13. #include "proto/termlib.pro"
  14.  
  15. #ifndef AMIGA
  16. # include <sgtty.h>
  17. #endif
  18.  
  19. static int    getent __PARMS((char *, char *, FILE *, int));
  20. static int    nextent __PARMS((char *, FILE *, int));
  21. static int    _match __PARMS((char *, char *));
  22. static char    *_addfmt __PARMS((char *, char *, int));
  23. static char    *_find __PARMS((char *, char *));
  24.  
  25. /*
  26.  * Global variables for termlib
  27.  */
  28.  
  29. char    *tent;                /* Pointer to terminal entry, set by tgetent */
  30. char    PC = 0;               /* Pad character, default NULL */
  31. char    *UP = 0, *BC = 0;     /* Pointers to UP and BC strings from database */
  32. short    ospeed;               /* Baud rate (1-16, 1=300, 16=19200), as in stty */
  33.  
  34. /*
  35.  * Module: tgetent
  36.  *
  37.  * Purpose: Get termcap entry for <term> into buffer at <tbuf>.
  38.  *
  39.  * Calling conventions: char tbuf[TBUFSZ+], term=canonical name for
  40.  *            terminal.
  41.  *
  42.  * Returned values: 1 = success, -1 = can't open file,
  43.  *            0 = can't find terminal.
  44.  *
  45.  * Notes
  46.  *        Should probably supply static buffer.
  47.  *
  48.  *        Uses environment variables "TERM" and
  49.  *    "TERMCAP". If TERM = term (that is, if the argument
  50.  *    matches the environment) then it looks at TERMCAP.
  51.  *        If TERMCAP begins with a slash, then it assumes
  52.  *    this is the file to search rather than /etc/termcap.
  53.  *        If TERMCAP does not begin with a slash, and it
  54.  *    matches TERM, then this is used as the entry.
  55.  *
  56.  *        This could be simplified considerably for non-UNIX
  57.  *    systems.
  58.  */
  59.  
  60. #ifdef AMIGA
  61. # define TERMCAPFILE "s:termcap"
  62. #else
  63. # define TERMCAPFILE "/etc/termcap"
  64. #endif
  65.  
  66. tgetent(tbuf, term)
  67.     char    *tbuf;               /* Buffer to hold termcap entry, TBUFSZ bytes max */
  68.     char    *term;               /* Name of terminal */
  69. {
  70.     char    tcbuf[32];           /* Temp buffer to handle */
  71.     char    *tcptr = tcbuf;      /* extended entries */
  72.     char    *tcap = TERMCAPFILE; /* Default termcap file */
  73.     char    *tmp;
  74.     FILE    *termcap;
  75.     int        retval = 0;
  76.     int        len;
  77.  
  78.     if ((tmp = (char *)vimgetenv("TERMCAP")) != NULL)
  79.     {
  80.         if (*tmp == '/')            /* TERMCAP = name of termcap file */
  81.             tcap = tmp ;
  82.         else                        /* TERMCAP = termcap entry itself */
  83.         {
  84.             int tlen = strlen(term);
  85.  
  86.             while (*tmp && *tmp != ':') /* Check if TERM matches */
  87.             {
  88.                 while (*tmp == '|')
  89.                     tmp++;
  90.                 if (_match(tmp, term) == tlen)
  91.                 {
  92.                     strcpy(tbuf, tmp);
  93.                     tent = tbuf;
  94.                     return 1;
  95.                 } 
  96.                 else
  97.                     tmp = _find(tmp, ":|");
  98.             }
  99.         }
  100.     }
  101.     if (!(termcap = fopen(tcap, "r")))
  102.     {
  103.         strcpy(tbuf, tcap);
  104.         return -1;
  105.     }
  106.  
  107.     len = 0;
  108.     while (getent(tbuf + len, term, termcap, TBUFSZ - len))
  109.     {
  110.         if ((term = tgetstr("tc", &tcptr)))         /* extended entry */
  111.         {
  112.             rewind(termcap);
  113.             len = strlen(tbuf);
  114.         }
  115.         else
  116.         {
  117.             retval = 1; 
  118.             tent = tbuf;
  119.             break;
  120.         }
  121.     }
  122.     fclose(termcap);
  123.     return retval;
  124. }
  125.  
  126.     static int
  127. getent(tbuf, term, termcap, buflen)
  128.     char    *tbuf, *term;
  129.     FILE    *termcap;
  130.     int        buflen;
  131. {
  132.     char    *tptr;
  133.     int        tlen = strlen(term);
  134.  
  135.     while (nextent(tbuf, termcap, buflen))   /* For each possible entry */
  136.     {
  137.         tptr = tbuf;
  138.         while (*tptr && *tptr != ':')    /* : terminates name field */
  139.         {
  140.             while (*tptr == '|')             /* | seperates names */
  141.                 tptr++;
  142.             if (_match(tptr, term) == tlen)             /* FOUND! */
  143.             {
  144.                 tent = tbuf;
  145.                 return 1;
  146.             } 
  147.             else                           /* Look for next name */
  148.                 tptr = _find(tptr, ":|");
  149.         }
  150.     }
  151.     return 0;
  152. }
  153.  
  154.     static int
  155. nextent(tbuf, termcap, buflen)         /* Read 1 entry from TERMCAP file */
  156.     char    *tbuf;
  157.     FILE    *termcap;
  158.     int        buflen;
  159. {
  160.     char *lbuf = tbuf;           /* lbuf=line buffer */
  161.                                  /* read lines straight into buffer */
  162.  
  163.     while (lbuf < tbuf+buflen &&                        /* There's room and */
  164.           fgets(lbuf, (int)(tbuf+buflen-lbuf), termcap))        /* another line */
  165.     {
  166.         int llen = strlen(lbuf);
  167.  
  168.         if (*lbuf == '#')                               /* eat comments */
  169.             continue;
  170.         if (lbuf[-1] == ':' &&                        /* and whitespace */
  171.             lbuf[0] == '\t' &&
  172.             lbuf[1] == ':')
  173.         {
  174.             strcpy(lbuf, lbuf+2);
  175.             llen -= 2;
  176.         }
  177.         if (lbuf[llen-2] == '\\')                  /* and continuations */
  178.             lbuf += llen-2;
  179.         else
  180.         {
  181.             lbuf[llen-1]=0;           /* no continuation, return */
  182.             return 1;
  183.         }
  184.     }
  185.  
  186.     return 0;                                    /* ran into end of file */
  187. }
  188.  
  189. /*
  190.  * Module: tgetflag
  191.  *
  192.  * Purpose: returns flag true or false as to the existence of a given
  193.  *        entry. used with 'bs', 'am', etc...
  194.  *
  195.  * Calling conventions: id is the 2 character capability id.
  196.  *
  197.  * Returned values: 1 for success, 0 for failure.
  198.  */
  199.  
  200. tgetflag(id)
  201.     char *id;
  202. {
  203.     char    buf[256], *ptr = buf;
  204.  
  205.     return tgetstr(id, &ptr) ? 1 : 0;
  206. }
  207.  
  208. /*
  209.  * Module: tgetnum
  210.  *
  211.  * Purpose: get numeric value such as 'li' or 'co' from termcap.
  212.  *
  213.  * Calling conventions: id = 2 character id.
  214.  *
  215.  * Returned values: -1 for failure, else numerical value.
  216.  */
  217.  
  218. tgetnum(id)
  219. char *id;
  220. {
  221.     char *ptr, buf[256];
  222.     ptr = buf;
  223.  
  224.     if (tgetstr(id, &ptr))
  225.         return atoi(buf);
  226.     else
  227.         return 0;
  228. }
  229.  
  230. /*
  231.  * Module: tgetstr
  232.  *
  233.  * Purpose: get terminal capability string from database.
  234.  *
  235.  * Calling conventions: id is the two character capability id.
  236.  *            (*buf) points into a hold buffer for the
  237.  *            id. the capability is copied into the buffer
  238.  *            and (*buf) is advanced to point to the next
  239.  *            free byte in the buffer.
  240.  *
  241.  * Returned values: 0 = no such entry, otherwise returns original
  242.  *            (*buf) (now a pointer to the string).
  243.  *
  244.  * Notes
  245.  *        It also decodes certain escape sequences in the buffer.
  246.  *    they should be obvious from the code:
  247.  *        \E = escape.
  248.  *        \n, \r, \t, \f, \b match the 'c' escapes.
  249.  *        ^x matches control-x (^@...^_).
  250.  *        \nnn matches nnn octal.
  251.  *        \x, where x is anything else, matches x. I differ
  252.  *    from the standard library here, in that I allow ^: to match
  253.  *    :.
  254.  *
  255.  */
  256.  
  257. char *
  258. tgetstr(id, buf)
  259. char    *id, **buf;
  260. {
  261.     int    len = strlen(id);
  262.     char *tmp=tent;
  263.     char *hold;
  264.     int        i;
  265.  
  266.     do {
  267.         tmp = _find(tmp, ":");                     /* For each field */
  268.         while (*tmp == ':')                        /* skip empty fields */
  269.             tmp++;
  270.         if (!*tmp)
  271.             break;
  272.  
  273.         if (_match(id, tmp) == len) {
  274.             tmp += len;                   /* find '=' '@' or '#' */
  275.             if (*tmp == '@')                  /* :xx@: entry for tc */
  276.                 return 0;                   /* deleted entry */
  277.             hold= *buf;
  278.             while (*++tmp && *tmp != ':') {/* not at end of field */
  279.                 switch(*tmp) {
  280.                 case '\\':            /* Expand escapes here */
  281.                     switch(*++tmp) {
  282.                     case 0:        /* ignore backslashes */
  283.                         tmp--;    /* at end of entry */
  284.                         break;   /* shouldn't happen */
  285.                     case 'e':
  286.                     case 'E':                     /* ESC */
  287.                         *(*buf)++ = '\033'; 
  288.                         break;
  289.                     case 'n':                      /* \n */
  290.                         *(*buf)++ = '\n'; 
  291.                         break;
  292.                     case 'r':                      /* \r */
  293.                         *(*buf)++ = '\r'; 
  294.                         break;
  295.                     case 't':                      /* \t */
  296.                         *(*buf)++ = '\t'; 
  297.                         break;
  298.                     case 'b':                      /* \b */
  299.                         *(*buf)++ = '\b'; 
  300.                         break;
  301.                     case 'f':                      /* \f */
  302.                         *(*buf)++ = '\f'; 
  303.                         break;
  304.                     case '0':                    /* \nnn */
  305.                     case '1': 
  306.                     case '2': 
  307.                     case '3': 
  308.                     case '4':
  309.                     case '5': 
  310.                     case '6': 
  311.                     case '7': 
  312.                     case '8': 
  313.                     case '9':
  314.                         **buf = 0;
  315.                             /* get up to three digits */
  316.                         for (i = 0; i < 3 && isdigit(*tmp); ++i)
  317.                             **buf = **buf * 8 + *tmp++ - '0';
  318.                         (*buf)++;
  319.                         tmp--;
  320.                         break;
  321.                     default:      /* \x, for all other x */
  322.                         *(*buf)++= *tmp;
  323.                     }
  324.                     break;
  325.                 case '^':              /* control characters */
  326.                     *(*buf)++ = *++tmp - '@'; 
  327.                     break;
  328.                 default: 
  329.                     *(*buf)++ = *tmp;
  330.                 }
  331.             }
  332.             *(*buf)++ = 0;
  333.             return hold;
  334.         }
  335.     } while (*tmp);
  336.  
  337.     return 0;
  338. }
  339.  
  340. /*
  341.  * Module: tgoto
  342.  *
  343.  * Purpose: decode cm cursor motion string.
  344.  *
  345.  * Calling conventions: cm is cursor motion string.
  346.  *            line, col, are the desired destination.
  347.  *
  348.  * Returned values: a string pointing to the decoded string, or
  349.  *            "OOPS" if it cannot be decoded.
  350.  *
  351.  * Notes
  352.  *        The accepted escapes are:
  353.  *            %d     as in printf, 0 origin.
  354.  *            %2, %3     like %02d, %03d in printf.
  355.  *            %.     like %c
  356.  *            %+x     adds <x> to value, then %.
  357.  *            %>xy     if value>x, adds y. No output.
  358.  *            %i     increments line& col, no output.
  359.  *            %r     reverses order of line&col. No output.
  360.  *            %%     prints as a single %.
  361.  *            %n     exclusive or row & col with 0140.
  362.  *            %B     BCD, no output.
  363.  *            %D     reverse coding (x-2*(x%16)), no output.
  364.  */
  365.  
  366. char *
  367. tgoto(cm, col, line)
  368. char    *cm;                                      /* cm string, from termcap */
  369. int    col,                                           /* column, x position */
  370.     line;                                            /* line, y position */
  371. {
  372.     char    gx, gy,                                           /*    x, y */
  373.         *ptr,                                     /* pointer in 'cm' */
  374.         reverse = 0,                                 /* reverse flag */
  375.         *bufp,                         /* pointer in returned string */
  376.         addup = 0,                                     /* add upline */
  377.         addbak = 0,                                    /* add backup */
  378.         c;
  379.     static char buffer[32];
  380.  
  381.     if (!cm)
  382.         return "OOPS";                       /* Kludge, but standard */
  383.  
  384.     bufp = buffer;
  385.     ptr = cm;
  386.  
  387.     while (*ptr) {
  388.         if ((c = *ptr++) != '%') {                     /* normal char */
  389.             *bufp++ = c;
  390.         } else {                                         /* % escape */
  391.             switch(c = *ptr++) {
  392.             case 'd':                                 /* decimal */
  393.                 bufp = _addfmt(bufp, "%d", line);
  394.                 line = col;
  395.                 break;
  396.             case '2':                         /* 2 digit decimal */
  397.                 bufp = _addfmt(bufp, "%02d", line);
  398.                 line = col;
  399.                 break;
  400.             case '3':                         /* 3 digit decimal */
  401.                 bufp = _addfmt(bufp, "%03d", line);
  402.                 line = col;
  403.                 break;
  404.             case '>':                      /* %>xy: if >x, add y */
  405.                 gx = *ptr++;
  406.                 gy = *ptr++;
  407.                 if (col>gx) col += gy;
  408.                 if (line>gx) line += gy;
  409.                 break;
  410.             case '+':                              /* %+c: add c */
  411.                 line += *ptr++;
  412.             case '.':                               /* print x/y */
  413.                 if (line == '\t' ||                /* these are */
  414.                    line == '\n' ||             /* chars that */
  415.                    line == '\004' ||             /* UNIX hates */
  416.                    line == '\0') {
  417.                     line++;         /* so go to next pos */
  418.                     if (reverse == (line == col))
  419.                         addup=1;      /* and mark UP */
  420.                     else
  421.                         addbak=1;           /* or BC */
  422.                 }
  423.                 *bufp++=line;
  424.                 line = col;
  425.                 break;
  426.             case 'r':                              /* r: reverse */
  427.                 gx = line; 
  428.                 line = col; 
  429.                 col = gx;
  430.                 reverse = 1;
  431.                 break;
  432.             case 'i':             /* increment (1-origin screen) */
  433.                 col++;
  434.                 line++;
  435.                 break;
  436.             case '%':                          /* %%=% literally */
  437.                 *bufp++='%';
  438.                 break;
  439.             case 'n':                       /* magic DM2500 code */
  440.                 line ^= 0140;
  441.                 col ^= 0140;
  442.                 break;
  443.             case 'B':                            /* bcd encoding */
  444.                 line = line/10<<4+line%10;
  445.                 col = col/10<<4+col%10;
  446.                 break;
  447.             case 'D':                   /* magic Delta Data code */
  448.                 line = line-2*(line&15);
  449.                 col = col-2*(col&15);
  450.                 break;
  451.             default:                           /* Unknown escape */
  452.                 return "OOPS";
  453.             }
  454.         }
  455.     }
  456.  
  457.     if (addup)                                              /* add upline */
  458.         if (UP) {
  459.             ptr=UP;
  460.             while (isdigit(*ptr) || *ptr == '.')
  461.                 ptr++;
  462.             if (*ptr == '*')
  463.                 ptr++;
  464.             while (*ptr)
  465.                 *bufp++ = *ptr++;
  466.         }
  467.  
  468.     if (addbak)                                          /* add backspace */
  469.         if (BC) {
  470.             ptr=BC;
  471.             while (isdigit(*ptr) || *ptr == '.')
  472.                 ptr++;
  473.             if (*ptr == '*')
  474.                 ptr++;
  475.             while (*ptr)
  476.                 *bufp++ = *ptr++;
  477.         } 
  478.         else
  479.             *bufp++='\b';
  480.  
  481.     *bufp = 0;
  482.  
  483.     return(buffer);
  484. }
  485.  
  486. /*
  487.  * Module: tinit
  488.  *
  489.  * Purpose: simplified terminal initialisation.
  490.  *
  491.  * Calling conventions: name is name of terminal.
  492.  *
  493.  * Returned values: none.
  494.  *
  495.  * Notes
  496.  *        tinit calls tgetent, then sets up the global
  497.  *    variables PC, UP, BC, ospeed appropriately.
  498.  *
  499.  */
  500.  
  501. #if 0        /* already included in term.c */
  502.  
  503. char tbuf[TBUFSZ];                                /* Buffer for termcap entry */
  504. char junkbuf[TBUFSZ];                                  /* Big buffer for junk */
  505. char *junkptr;
  506.  
  507. tinit(name)
  508. char *name;
  509. {
  510. #ifndef AMIGA
  511.     struct sgttyb sgbuf;
  512. #endif
  513.     char *ps;
  514.  
  515.     junkptr = junkbuf;
  516.  
  517.     tgetent(tbuf, name);
  518.  
  519.     ps = tgetstr("pc", &junkptr);
  520.     if (ps) PC = *ps;
  521.     UP = tgetstr("up", &junkptr);
  522.     BC = tgetstr("bc", &junkptr);
  523.  
  524. #ifdef AMIGA
  525.     ospeed=0;
  526. #else
  527.     gtty(1, &sgbuf);
  528.     ospeed=sgbuf.sg_ospeed;
  529. #endif
  530.     return 0;
  531. }
  532. #endif
  533.  
  534. /*
  535.  * Module: tputs
  536.  *
  537.  * Purpose: decode padding information
  538.  *
  539.  * Calling conventions: cp = string to be padded, affcnt = # of items
  540.  *            affected (lines, characters, whatever),
  541.  *            outc = routine to output 1 character.
  542.  *
  543.  * Returned values: none
  544.  *
  545.  * Notes
  546.  *        cp has padding information ahead of it, in the form
  547.  *    nnnTEXT or nnn*TEXT. nnn is the number of milliseconds to delay,
  548.  *    and may be a decimal (nnn.mmm). If the asterisk is given, then
  549.  *    the delay is multiplied by afcnt. The delay is produced by outputting
  550.  *    a number of nulls (or other padding char) after printing the
  551.  *    TEXT.
  552.  *
  553.  */
  554.  
  555. long _bauds[16]={
  556.     0,    50,    75,    110,
  557.     134,    150,    200,    300,
  558.     600,    1200,    1800,    2400,
  559.     4800,    9600,    19200,    19200 };
  560.  
  561. tputs(cp, affcnt, outc)
  562. char *cp;                                                 /* string to print */
  563. int affcnt;                                      /* Number of lines affected */
  564. void (*outc) __ARGS((unsigned int));                              /* routine to output 1 character */
  565. {
  566.     long    frac,                    /* 10^(#digits after decimal point) */
  567.         counter,                                           /* digits */
  568.         atol();
  569.  
  570.     if (isdigit(*cp)) {
  571.         counter = 0;
  572.         frac = 1000;
  573.         while (isdigit(*cp))
  574.             counter = counter * 10L + (long)(*cp++ - '0');
  575.         if (*cp == '.')
  576.             while (isdigit(*++cp)) {
  577.                 counter = counter * 10L + (long)(*cp++ - '0');
  578.                 frac = frac * 10;
  579.             }
  580.         if (*cp!='*') {                 /* multiply by affected lines */
  581.             if (affcnt>1) affcnt = 1;
  582.         } 
  583.         else
  584.             cp++;
  585.  
  586.         /* Calculate number of characters for padding counter/frac ms delay */
  587.         if (ospeed)
  588.             counter = (counter * _bauds[ospeed] * (long)affcnt) / frac;
  589.  
  590.         while (*cp)                                  /* output string */
  591.             (*outc)(*cp++);
  592.         if (ospeed)
  593.             while (counter--)            /* followed by pad characters */
  594.                 (*outc)(PC);
  595.     } 
  596.     else
  597.         while (*cp)
  598.             (*outc)(*cp++);
  599.     return 0;
  600. }
  601.  
  602. /*
  603.  * Module: tutil.c
  604.  *
  605.  * Purpose: Utility routines for TERMLIB functions.
  606.  *
  607.  */
  608.  
  609.     static int
  610. _match(s1, s2)                 /* returns length of text common to s1 and s2 */
  611. char *s1, *s2;
  612. {
  613.     int i = 0;
  614.  
  615.     while (s1[i] && s1[i] == s2[i])
  616.         i++;
  617.  
  618.     return i;
  619. }
  620.  
  621.     static char *
  622. _find(s, set)   /* finds next c in s that's a member of set, returns pointer */
  623. char *s, *set;
  624. {
  625.     for(; *s; s++) {
  626.         char    *ptr = set;
  627.  
  628.         while (*ptr && *s != *ptr)
  629.             ptr++;
  630.  
  631.         if (*ptr)
  632.             return s;
  633.     }
  634.  
  635.     return s;
  636. }
  637.  
  638.     static char *
  639. _addfmt(buf, fmt, val)             /* add val to buf according to format fmt */
  640. char *buf, *fmt;
  641. int val;
  642. {
  643.     sprintf(buf, fmt, val);
  644.     while (*buf)
  645.         buf++;
  646.     return buf;
  647. }
  648.